<template>
  <div class="out-line-container">
    <v-contextmenu ref="ctmRef" :disabled="ctmDisabled">
      <v-contextmenu-item @click="handleCopy">复制</v-contextmenu-item>
      <template v-if="canPaste">
        <v-contextmenu-item @click="handlePastePrev">前方粘贴同级内容</v-contextmenu-item>
        <v-contextmenu-item @click="handlePasteNext">后方粘贴同级内容</v-contextmenu-item>
      </template>
    </v-contextmenu>

    <el-tree
      node-key="dataId"
      ref="outlineTreeRef"
      :data="outlineData"
      :props="defaultProps"
      :default-expand-all="true"
      :expand-on-click-node="false"
      @node-click="handleNodeClick"
    >
      <span class="custom-tree-node"
          slot-scope="{ node, data }"
          v-contextmenu:ctmRef
          @contextmenu.prevent="handleContextMenu(data)"
        >
        <span :title="getNodeLabel(data)" :style="getStyle(node)">{{ getNodeLabel(data) }}</span>
        <span v-if="canManage">
          <el-dropdown v-if="data.key === 0" @command="handleCommand" @click.stop.prevent>
            <span class="el-dropdown-link" @click.stop.prevent>
              <i class="el-icon-circle-plus-outline"></i>
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item v-for="item of addableTypes" :command="item.key" :key="item.key"
                >{{ item.label }}
                <template v-if="item.key === 27">(横)</template>
                <template v-if="item.key === 26">(竖)</template>
              </el-dropdown-item>
            </el-dropdown-menu>
          </el-dropdown>
          <el-button v-if="isRemoveable(data, node)" icon="el-icon-delete" @click.stop="remove(data)"> </el-button>
        </span>
      </span>
    </el-tree>
  </div>
</template>

<script>
import { cloneDeep } from 'lodash'
import { mapGetters } from 'vuex'
import { scrollPageByData, getCopyedData, getNextIndexList } from '@/utils/helpers'
import { contentTypes, flatPrototypes, isAnnexByType } from '@/constants/structure'
import addPage, { addPagesByData, replacePageByData } from '@/utils/commands/addPage'
import { removeCheck } from '@/api'
import removePage from '@/utils/commands/removePage'
import { Message, MessageBox } from 'element-ui'
import { getLocalPageList } from '@/utils/save'

export default {
  name: 'Outline',
  data() {
    return {
      copyedData: null,
      canPaste: false,
      ctmDisabled: false,
      tempData: null,
      tempPageList: [],
      defaultProps: {
        children: 'children'
      }
    }
  },
  computed: {
    en() {
      return this.$store.getters.languageType === 'both'
    },
    requiredMap() {
      return this.ruleStructures.reduce((acc, cur) => ((acc[cur.sectionType] = cur.required), acc), {})
    },
    canManage() {
      if (this.entry) return false //  即为 flow, preview，只有当默认没有的时候才可以调整
      // 结构化块或个人收藏时，没有文档的结构可以处理
      if (this.editContentType === 'block' || this.editContentType === 'collect') return false //  document | block | collect
      return this.editContentType === 'template' || this.isChief
    },
    currentPageId() {
      return this.$store.state.xmleditor.currentPageId
    },
    outlineData() {
      const children = this.$store.state.xmleditor.pageData
      const standardName = this.$store.state.xmleditor.standardBaseInfo?.standardName || '--'

      // html预览时显示图和表
      if (this.$store.getters.entry === 'htmlpreview') {
        const figures = []
        const tables = []
        this.$store.state.xmleditor.editorPageList.forEach((item) => {
          let prefix = ''
          if (item.pageType === 'introduction') {
            prefix = '0.'
          } else if (item.pageType.endsWith('Annex')) {
            prefix = item.blockList[0]?.textData?.contentList?.[0]?.contentNumber + '.'
          }
          item.blockList.forEach((i) => {
            if (i.dataType === 'figure') {
              figures.push({
                ...i,
                label: `图-${prefix}${i.figureData?.figureNumber} ${i.figureData?.captionZh?.title}`
              })
            }
            if (i.dataType === 'table') {
              tables.push({ ...i, label: `表-${prefix}${i.tableData?.tableNumber} ${i.tableData?.captionZh?.title}` })
            }
          })
        })
        return [
          { label: standardName, disabled: true, key: 0, children },
          { label: '图', disabled: true, key: 500, children: figures },
          { label: '表', disabled: true, key: 600, children: tables }
        ]
      }

      return [{ label: standardName, disabled: true, key: 0, children }]
    },
    ...mapGetters(['addableTypes', 'isChief', 'editContentType', 'entry', 'ruleStructures'])
  },
  watch: {
    currentPageId(val) {
      this.$refs.outlineTreeRef.setCurrentKey(val)
    }
  },
  mounted() {
    window.addEventListener('innerEditorClick', this.windowClickHandler)
  },
  destroyed() {
    window.removeEventListener('innerEditorClick', this.windowClickHandler)
  },
  methods: {
    windowClickHandler() {
      this.$refs.ctmRef.hide()
    },
    handleCopy() {
      this.tempPageList = getLocalPageList()
      this.copyedData = this.tempData
    },
    getTempPageList() {
      const pageList = getLocalPageList()
      let currentPage = null
      for (let i = 0; i < pageList.length; i++) {
        // 不停止的逻辑：
        // 1. 当前页面有blockId等于源blockId
        // 2. 前一页的blockId等于源blockId，且当前页是 blank_page
        // 3. 前一页是 blank_page，且当前页是 blank_page
        const page = pageList[i]
        if(page.pageType === 'content') {
          if (page.blockList?.[0]?.dataType === 'blank_page') {
            const blocks = page.blockList.filter(i => i.dataType !== 'blank_page')
            currentPage.blockList.push(...blocks)
          } else {
            currentPage = page
          }
        }
      }

      return pageList.filter(i => i.blockList?.[0]?.dataType !== 'blank_page')
    },
    getSourceBlocks() {
      // 1. 找到源页面
      let sourceBlocks = []
      let breakFlag = false
      let pushFlag = undefined
      const pageList = this.getTempPageList()
      const nextIndexList = getNextIndexList(this.getIndex(this.copyedData))

      for (let i = 0; i < pageList.length; i++) {
        // 不停止的逻辑：
        // 1. 当前页面有blockId等于源blockId
        // 2. 前一页的blockId等于源blockId，且当前页是 blank_page
        // 3. 前一页是 blank_page，且当前页是 blank_page
        if (breakFlag) {
          break
        }
        for (let j = 0; j < pageList[i].blockList?.length; j++) {
          const block = pageList[i].blockList[j]
          if (block.blockId === this.copyedData.blockId && /item[1-4]/g.test(block.dataType)) {
            breakFlag = true
            // 如果等于源blockId，则添加到sourceBlocks
            pushFlag = true
          }

          // 如果到next index，则停止循环
          if (nextIndexList.includes(this.getIndex(block))) {
            pushFlag = false
            breakFlag = true
          }

          if (pushFlag) {
            sourceBlocks.push(block)
          } else if (pushFlag === false) {
            break
          }
        }
      }
      if (!sourceBlocks.length) {
        console.debug('粘贴异常')
      }
      const data = sourceBlocks.map(getCopyedData)
      return data
    },
    getBeforeTargetPage() {
      this.tempPageList = getLocalPageList()
      const targetPageIndex = this.tempPageList.findIndex(item => {
        return item.blockList.some(i => i.blockId === this.tempData.blockId && /item[1-4]/g.test(i.dataType))
      })
      if (targetPageIndex < 0) {
        return console.debug('粘贴异常，目标页不存在')
      }
      const targetPage = cloneDeep(this.tempPageList[targetPageIndex])
      return { targetPage, targetPageIndex }
    },
    getAfterTargetPage() {
      this.tempPageList = getLocalPageList()
      const nextIndexList = getNextIndexList(this.getIndex(this.tempData))

      let targetPage = null
      let targetPageIndex = -1
      let targetBlockIndex = null
      let targetStartPageIndex = -1
      let breakFlag = false
      let startItemx = false

      for (let i = 0; i < this.tempPageList.length; i++) {
        if (breakFlag) {
          break
        }
        const page = this.tempPageList[i]
        if (startItemx) {
          if (page?.blockList?.[0]?.dataType === 'blank_page') {
            targetStartPageIndex = i
          } else {
            startItemx = false
          }
        }
        for (let j = 0; j < page.blockList?.length; j++) {
          const block = page.blockList[j]
          if (block.blockId === this.tempData.blockId && /item[1-4]/g.test(block.dataType)) {
            targetStartPageIndex = i
            startItemx = true
          }
          // 如果到next index，则停止循环
          if (nextIndexList.includes(this.getIndex(block))) {
            targetPage = page
            targetPageIndex = i
            targetBlockIndex = j
            breakFlag = true
            break
          }
        }
      }

      if (targetPageIndex === -1) {
        if (targetStartPageIndex > -1) {
          targetPageIndex = targetStartPageIndex
        } else {
          targetPageIndex = this.tempPageList.length - 1
        }
        targetPage = this.tempPageList[targetPageIndex]
        targetBlockIndex = targetPage?.blockList?.length
      }

      return { targetPage, targetPageIndex, targetBlockIndex }
    },
    // dataIndex更准确
    getIndex(data) {
      return data?.dataIndex || data?.blockIndex
    },
    /** 粘贴章节 */
    handlePasteChapter(step) {
      const sourcePageIndex = this.tempPageList.findIndex(item => item.pageId === this.copyedData.pageId)
      let targetPageIndex = this.tempPageList.findIndex(item => item.pageId === this.tempData.pageId)

      if (sourcePageIndex < 0 || targetPageIndex < 0) {
        return console.debug('粘贴异常')
      }

      const sourcePages = [this.tempPageList[sourcePageIndex]]
      if (this.tempPageList[sourcePageIndex+1]?.blockList?.[0]?.dataType === 'blank_page') {
        sourcePages.push(this.tempPageList[sourcePageIndex+1])
        if (step) {
          targetPageIndex++
        }
        let idx = sourcePageIndex + 2
        while(this.tempPageList[idx]?.blockList?.[0]?.dataType === 'blank_page') {
          sourcePages.push(this.tempPageList[idx])
          if (step) {
            targetPageIndex++
          }
          idx++
        }
      }

      addPagesByData(targetPageIndex + step, sourcePages.map(getCopyedData))
    },
    /** 在目标条标题前粘贴 */
    pasteBeforeItemx(data) {
      const { targetPage, targetPageIndex } = this.getBeforeTargetPage()
      const targetBlockIndex = targetPage.blockList.findIndex(item => item.blockId === this.tempData.blockId && /item[1-4]/g.test(item.dataType))
      if (targetBlockIndex < 0) {
        return console.debug('粘贴异常，目标位置未找到')
      }
      targetPage.blockList.splice(targetBlockIndex, 0, ...data)
      // 3. 找到目标页面，整体替换
      replacePageByData(targetPageIndex, targetPage)
    },

    /** 在目标条标题后粘贴 */
    pasteAfterItemx(data) {
      const { targetPage, targetPageIndex, targetBlockIndex } = this.getAfterTargetPage()
      targetPage.blockList.splice(targetBlockIndex, 0, ...data)
      // 3. 找到目标页面，整体替换
      replacePageByData(targetPageIndex, targetPage)
    },
    /**
     * 粘贴条标题
     * 在目标页面 blockList 增加对应的内容
     * 如果是在前面，则直接插入
     * 如果是在后面，则要判断是否有子节点、空白页，或者直接找到下一个节点或更高维度的章条
     */
    handlePasteItemx(step) {
      const data = this.getSourceBlocks()
      if (step === 0) {
        this.pasteBeforeItemx(data)
      } else {
        this.pasteAfterItemx(data)
      }
    },
    handlePaste(step) {
      if (this.copyedData.dataType === 'chapter') {
        this.handlePasteChapter(step)
      } else {
        this.handlePasteItemx(step)
      }
    },
    handlePastePrev() {
      this.handlePaste(0)
    },
    handlePasteNext() {
      this.handlePaste(1)
    },
    handleContextMenu(data) {
      // 仅这些章节和题条可以复制
      // 注意：应将这些章节或题条后的空白页中的内容也一并复制
      if (['chapter', 'item1', 'item2', 'item3', 'item4'].includes(data.dataType)) {
        this.canPaste = this.copyedData && this.copyedData.dataType === data?.dataType
        this.tempData = data
        this.ctmDisabled = false
      } else {
        this.ctmDisabled = true
        this.canPaste = false
        Message.warning('该内容不支持复制')
      }
    },
    checkEditable(node) {
      if (this.editContentType === 'template') return true
      if (this.$store.getters.entry === 'htmlpreview') return true // html预览
      const pageId = node.data.pageId || node.data.blockId // blockId是对标题一
      if (pageId) {
        const editableIds = this.$store.state.xmleditor.editableIds
        if (editableIds.includes(pageId)) {
          return true
        }
        if (node?.data?.pageType === 'contents') {
          return !!editableIds?.length
        }
        let current = node
        while (current.parent) {
          if ([current.data.blockId, current.data.pageId].some((id) => editableIds.includes(id))) {
            return true
          }
          current = current.parent
        }
      }
      return false
    },
    getStyle(node) {
      return this.checkEditable(node) ? '' : { color: '#ccc' }
    },
    getNodeLabel(data) {
      const { dataType, pageType, dataIndex, title, label, textData } = data
      // 1. 附录
      if (isAnnexByType(pageType)) {
        return data?.title?.replace(/&nbsp;/g, '')
      }
      // 2. 封面, 签署页, 更改记录页, 目次, 前言, 引言, 参考文献, 索引
      if (pageType) {
        return '- ' + title
      }
      // 图片或表格
      if (dataType === 'figure' || dataType === 'table') {
        return label
      }
      // 4. 条
      if (dataType) {
        return `${dataIndex} ${textData.contentList?.map((i) => i.fullContent).join('')}`
      }
      // 其他(最外层级)
      return label
    },
    isRemoveable(data, node) {
      if (data.key === 0) return false //  template
      // if (data.pageType === 'index' || data.pageType === 'indexEn') return false //  中文索引、英文索引手动不能删除
      // if (this.editContentType === 'template') return true //  template
      // 文档时不是主编，不能编辑
      if (this.editContentType === 'document' && !this.isChief) return false

      // 如果后端配置的规则有值时
      if (this.ruleStructures.length) {
        if (data.dataType !== 'chapter') {
          if (this.requiredMap[data.dataType || data.pageType]) return false
        }
      } else {
        // 后端没有配置默认按照0004D规范，不可删除的条件
        const prototype = flatPrototypes.find(({ pageType, dataType }) => {
          return (pageType || dataType) === (data.pageType || data.dataType)
        })
        // 1. disable为true或
        if (data.disabled || prototype?.disabled) return false
      }
      if (data.pageType === 'contents') {
        return this.checkEditable(node)
      }
      // 2. disabled为 false或undefined 同时 chapter是最后一个章节
      // const chptrSelector = '#tinymce>.page-wrapper .page-sheet[data-content-page-type="chapter"]'
      // const chptrPages = tinymce.activeEditor?.dom?.select(chptrSelector)
      // const isLastChapter = data.dataType === 'chapter' && chptrPages?.length === 1
      // 3. 题条
      const isItem = /item[1-4]/g.test(data.dataType)
      return !isItem && this.checkEditable(node)
    },
    handleNodeClick(data) {
      scrollPageByData(data)
    },
    handleCommand(type) {
      addPage(type)
    },
    async remove(data) {
      try {
        await MessageBox.confirm(`将删除${data.titleZh}，是否继续`, '提示', { type: 'warning' })
        const { pageType, dataType } = data
        if (pageType || contentTypes.includes(dataType)) {
          // 只有文档进行检查
          if (this.editContentType === 'document') {
            const res = await removeCheck(data.pageId)
            if (res?.data === false) {
              Message.error('经检查，该章节不能被删除!')
              return
            }
          }
          removePage(data)
        } else if (dataType) {
          // 参照竞品不对节点做处理，此处放开可能有异常需排查
          console.debug('---todo---')
          // removeNode(data)
        } else {
          throw new Error('错误的删除操作')
        }
      } catch (error) {
        console.log('已取消删除')
      }
    }
  }
}
</script>
<style scoped>
.out-line-container {
  position: relative;
  display: flex;
  flex-direction: column;
  height: 100%;
}
.out-line-container .el-tree {
  flex: 1;
  padding: 10px 0;
  background-color: transparent;
  overflow: auto;
}

.out-line-container .el-tree::-webkit-scrollbar-track-piece {
  background: #d4dbe3;
}

.out-line-container .el-tree::-webkit-scrollbar {
  width: 6px;
}

.out-line-container .el-tree::-webkit-scrollbar-thumb {
  background: #bac3cf;
  border-radius: 6px;
}

.out-line-container .el-tree .el-tree__empty-block {
  text-align: center;
  font-size: 14px;
  color: #888;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content {
  height: 30px;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node {
  line-height: 30px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05) !important;
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  white-space: nowrap;
  width: unset;
  margin-right: 10px;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node.locked {
  color: #b9b9b9;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node.editing {
  color: #67c23a;
  font-weight: 700;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node.editing .el-button {
  color: #67c23a;
  font-weight: 200;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node .el-input {
  width: 100%;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node .el-input input {
  border: 0;
  background-color: transparent;
  height: 25px;
  line-height: 25px;
  padding: 0;
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node .el-input input:focus {
  color: #00f;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node .el-dropdown {
  font-size: 12px;
  margin-right: 5px;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div {
  display: flex;
  align-items: center;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .bk {
  margin-right: 5px;
  position: relative;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .bk:before {
  position: absolute;
  left: -10px;
  top: -6px;
  transform: scale(0.65);
  color: #62a3eb;
  font-weight: 400;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .bk.specs:before {
  content: '规';
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .bk.means:before {
  content: '资';
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .label {
  cursor: pointer;
  flex: 1;
  padding-right: 10px;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  -webkit-line-clamp: 1;
  width: unset;
  max-width: 140px;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div button {
  cursor: pointer;
  padding: 3px !important;
  margin: 0;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div button.active {
  color: #5fdd5f;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .tree-menu {
  border: 0;
  min-height: unset !important;
  overflow: hidden;
  background-color: transparent;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > div .tree-menu .el-submenu {
  background-color: transparent;
}

.out-line-container
  .el-tree
  .el-tree-node
  .el-tree-node__content
  .custom-tree-node
  > div
  .tree-menu
  .el-submenu
  .el-submenu__title {
  font-size: 12px;
  height: 30px;
  line-height: 30px;
  border: 0;
  padding: 0;
  color: #888;
  cursor: pointer;
}

.out-line-container
  .el-tree
  .el-tree-node
  .el-tree-node__content
  .custom-tree-node
  > div
  .tree-menu
  .el-submenu
  .el-submenu__title
  .el-submenu__icon-arrow {
  color: #888;
  font-size: smaller;
}

.out-line-container
  .el-tree
  .el-tree-node
  .el-tree-node__content
  .custom-tree-node
  > div
  .tree-menu
  .el-submenu
  .el-submenu__title:hover,
.out-line-container .el-tree .el-tree-node.is-current > .el-tree-node__content {
  background-color: transparent;
}

.out-line-container .el-tree .el-tree-node.is-current > .el-tree-node__content .custom-tree-node {
  color: #00f;
  font-weight: 700;
}

.out-line-container .el-tree .el-tree-node .el-tree-node__content .custom-tree-node > span {
  max-width: 155px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.out-line-container .el-tree .el-button {
  padding: 3px 5px !important;
  line-height: 1;
}

.out-line-container .el-icon-circle-plus-outline {
  color: #025da4;
  font-size: 14px;
}
</style>
